package com.dss.auto_deploy.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dss.auto_deploy.config.WebsocketEndPoint;
import com.dss.auto_deploy.constants.AutoConstants;
import com.dss.auto_deploy.domain.AdmDocker;
import com.dss.auto_deploy.domain.AdmDockerRegistry;
import com.dss.auto_deploy.domain.Server;
import com.dss.auto_deploy.dto.AdmDockerRegistryAddDto;
import com.dss.auto_deploy.dto.AdmDockerRegistryPageDto;
import com.dss.auto_deploy.dto.AdmDockerRegistryUpdateDto;
import com.dss.auto_deploy.dto.WsSendDto;
import com.dss.auto_deploy.mapper.AdmDockerRegistryMapper;
import com.dss.auto_deploy.service.IAdmDockerRegistryService;
import com.dss.auto_deploy.service.IAdmDockerService;
import com.dss.auto_deploy.service.IServerService;
import com.dss.auto_deploy.util.*;
import com.dss.auto_deploy.vo.AdmDockerRegistryVo;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.model.Container;
import com.jcraft.jsch.Session;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;

/**
* @Description:docker仓库Service实现类
* @author dongshoushan
* @date 2022-09-22 14:39:03
*/
@Service
@Slf4j
@RequiredArgsConstructor(onConstructor = @_(@Autowired))
public class AdmDockerRegistryServiceImpl  extends ServiceImpl<AdmDockerRegistryMapper,AdmDockerRegistry> implements IAdmDockerRegistryService {

    /**
    *
    * docker仓库Mapper
    */
    private final AdmDockerRegistryMapper admDockerRegistryMapper;
    private final ThreadPoolTaskExecutor threadPoolTaskExecutor;
    private final RemoteSshUtil remoteSshUtil;
    private final DockerUtil dockerUtil;
    private final IAdmDockerService admDockerService;
    private final IServerService serverService;
    private final String baseLocalPath =  System.getProperty("user.dir")+ "/temp/config/registry";
    private final WebsocketEndPoint websocketEndPoint;
    private final CacheUtils cacheUtils;

    /**
    * 新增docker仓库
    * @param admDockerRegistryAddDto
    * @return Long
    */
    @Override
    public int insert(AdmDockerRegistryAddDto admDockerRegistryAddDto){
        AdmDockerRegistry admDockerRegistry = BeanUtils.copy(admDockerRegistryAddDto, AdmDockerRegistry.class);
        admDockerRegistry.setCreateTime(new Date());
        admDockerRegistry.setUpdateTime(new Date());
        int insert = admDockerRegistryMapper.insert(admDockerRegistry);
        if (insert == 1) {
            log.info("新增成功{}", JacksonUtils.toString(admDockerRegistry));
        }
        cacheUtils.deleteObject("getAllAdmDockerRegistry");
        return insert;
    }


    /**
    * 修改docker仓库
    * @param admDockerRegistryUpdateDto
    * @return Long
    */
    @Override
    public int update(AdmDockerRegistryUpdateDto admDockerRegistryUpdateDto){
        AdmDockerRegistry admDockerRegistry = BeanUtils.copy(admDockerRegistryUpdateDto, AdmDockerRegistry.class);
        int number = admDockerRegistryMapper.updateById(admDockerRegistry);
        if (number == 1) {
            log.info("修改成功{}",JacksonUtils.toString(admDockerRegistry));
        }
        cacheUtils.deleteObject("getAllAdmDockerRegistry");
        return number;
    }


    /**
    * 批量删除docker仓库
    * @param ids
    * @return Long
    */
    @Override
    public void deleteBatch(Long[] ids){
        this.removeByIds(Arrays.asList(ids));
        cacheUtils.deleteObject("getAllAdmDockerRegistry");
    }

    /**
    * docker仓库分页查询
    *
    * @param pageDto
    * @return List<AdmDockerRegistry>
    */
    @Override
    public Page<AdmDockerRegistryVo> list(AdmDockerRegistryPageDto pageDto) throws InterruptedException {
        Page<AdmDockerRegistry> tablesPage = new Page<>(pageDto.getPage(), pageDto.getLimit());
        Page<AdmDockerRegistry> pageSelectResult = admDockerRegistryMapper.selectPage(tablesPage, Wrappers.lambdaQuery(AdmDockerRegistry.class)
                .like(StringUtils.isNotEmpty(pageDto.getContent()),AdmDockerRegistry::getRegistryName, pageDto.getContent())
                .orderByDesc(AdmDockerRegistry::getCreateTime)
        );
        List<AdmDockerRegistry> records = pageSelectResult.getRecords();
        List<AdmDockerRegistryVo> voList = convertToVo(records);
        Page<AdmDockerRegistryVo> page = new Page<>();
        page.setRecords(voList);
        page.setTotal(pageSelectResult.getTotal());
        page.setSize(pageSelectResult.getSize());
        return page;
    }

    public List<AdmDockerRegistryVo> convertToVo(List<AdmDockerRegistry> list) throws InterruptedException {
        if (list == null || list.isEmpty()){
            return new ArrayList<>();
        }
        List<AdmDockerRegistryVo> registryVoList = BeanUtils.copyList(list, AdmDockerRegistryVo.class);
        List<Long> serverIds = registryVoList.stream().map(AdmDockerRegistryVo::getServerId).collect(Collectors.toList());
        List<Server> serverList = serverService.getAll().stream().filter(s->serverIds.contains(s.getId())).collect(Collectors.toList());
        List<AdmDocker> admDockerList = admDockerService.getAll().stream().filter(s->serverIds.contains(s.getServerId())).collect(Collectors.toList());
        Map<String, List<Container>> containerMap = admDockerService.getContainerMap(admDockerList);
        registryVoList.forEach(vo -> {
            Server serverVo = serverList.stream().filter(s -> s.getId().equals(vo.getServerId())).findFirst().orElse(null);
            String ip = serverVo.getIp();
            vo.setIp(ip);
            AdmDocker admDocker = admDockerList.stream().filter(s -> s.getServerId().equals(vo.getServerId())).findFirst().orElse(null);
            List<Container> containers = containerMap.get("AdmDockerVo" + admDocker.getId());
            Container container = containers.stream().filter(s -> s.getId().equals(vo.getContainerId())).findFirst().orElse(null);
            if (container == null) {
                vo.setState("未创建或超时");
            } else {
                vo.setState(container.getState());
            }
        });
        return registryVoList;
    }

    @Override
    public List<AdmDockerRegistry> getAll(String name) {
        List<AdmDockerRegistry> admDockers = admDockerRegistryMapper.selectList(Wrappers.lambdaQuery(AdmDockerRegistry.class)
                .like(StringUtils.isNotEmpty(name),AdmDockerRegistry::getRegistryName,name).orderByDesc(AdmDockerRegistry::getCreateTime));
        return admDockers;
    }

    @Override
    public List<AdmDockerRegistry> getAll() {
        if (cacheUtils.hasKey("getAllAdmDockerRegistry")) {
            return cacheUtils.getCacheObject("getAllAdmDockerRegistry");
        }else {
            List<AdmDockerRegistry> list = admDockerRegistryMapper.selectList(null);
            cacheUtils.setCacheObject("getAllAdmDockerRegistry", list,60*60*1000*24);
            return list;
        }
    }

    /**
     * 离线安装docker仓库
     * @param
     */
    @Override
    public void install(String ids, String tempPath, MultipartFile registryFile, MultipartFile registryWebFile) {
        long registryFileSize = registryFile.getSize();
        long registryWebFileSize = registryWebFile.getSize();
        String[] split = ids.split(",");
        CountDownLatch mainLatch = new CountDownLatch(split.length);
        for (String id : split) {
            threadPoolTaskExecutor.execute(() -> {
                AdmDockerRegistry dockerRegistry = admDockerRegistryMapper.selectById(id);
                Server server = serverService.getServerById(dockerRegistry.getServerId());
                AdmDocker admDocker = admDockerService.getByIp(server.getIp());
                //上传包
                try {
                    Session session = remoteSshUtil.connect(server);
                    System.out.println("开始上传仓库镜像文件");
                    //关闭防火墙
                    remoteSshUtil.exec(AutoConstants.DISABLE_FIREWALLD, session);
                    remoteSshUtil.exec(AutoConstants.STOP_FIREWALLD,session);
                    remoteSshUtil.exec(AutoConstants.AUTHORIZATION_FOLDER+tempPath,session);
                    CountDownLatch mainLatch2 = new CountDownLatch(2);
                    threadPoolTaskExecutor.execute(() -> {
                        try {
                            remoteSshUtil.putFile(session,registryFile,tempPath,registryFileSize,Long.parseLong(id),"registryInstall",server.getIp());
                        } catch (Exception e) {
                            throw new ServiceException(e.getMessage());
                        }finally {
                            mainLatch2.countDown();
                        }
                    });
                    threadPoolTaskExecutor.execute(() -> {
                        try {
                            remoteSshUtil.putFile(session,registryWebFile,tempPath,registryWebFileSize,Long.parseLong(id),"registryInstall",server.getIp());
                        } catch (Exception e) {
                            throw new ServiceException(e.getMessage());
                        }finally {
                            mainLatch2.countDown();
                        }
                    });
                    mainLatch2.await();
                    Thread.sleep(3000L);
                    String registryName = registryFile.getOriginalFilename();
                    String registryWebName = registryWebFile.getOriginalFilename();
                    sendMsg("loading",registryFile.getOriginalFilename());
                    sendMsg("loading",registryWebFile.getOriginalFilename());
                    //加载到镜像
                    remoteSshUtil.exec(StringUtils.format("docker load -i {}/{}",tempPath,registryName),session);
                    remoteSshUtil.exec(StringUtils.format("docker load -i {}/{}",tempPath,registryWebName),session);
                    Map map = new HashMap<String,Object>(16);
                    //生成配置文件并上传
                    String configLocalPath =baseLocalPath+"/"+dockerRegistry.getId()+"/config.yml";
                    String port = dockerRegistry.getPort().toString();
                    map.put("port",port);
                    FileUtils.generateFileByTemplate("registry-config.ftl", configLocalPath,baseLocalPath+"/"+dockerRegistry.getId(),map);
                    String storagePath = dockerRegistry.getStoragePath();
                    String configRemotePath = storagePath+"/registry";
                    remoteSshUtil.putFile(session, configLocalPath,configRemotePath);

                    //先删除再启动容器
                    DockerClient dockerClient = dockerUtil.getDockerClientByDocker(SpringUtils.getBean(AdmDockerServiceImpl.class).convertToVo(admDocker));
                    dockerUtil.delContainer(dockerClient, "registry-web");
                    dockerUtil.delContainer(dockerClient, "registry");

                    //安装docker仓库
                    String registryInfo = StringUtils.format("docker run -d -p {}:5000 --restart=always -v /etc/localtime:/etc/localtime --privileged=true --name registry -v {}:/var/lib/registry \\\n" +
                            "-v  {}/config.yml:/etc/docker/registry/config.yml  registry",port,storagePath,configRemotePath);
                    System.out.println(registryInfo);
                    String url = StringUtils.format("http://{}:{}",server.getIp(),dockerRegistry.getPort());
                    String[] split1 = dockerRegistry.getRegistryWebUrl().split(":");
                    String webPort = split1[split1.length - 1];
                    String registryWebInfo = StringUtils.format("docker run -d \\\n" +
                            "--name registry-web \\\n" +
                            "--restart=always \\\n" +
                            "-v /etc/localtime:/etc/localtime \\\n" +
                            "--link registry -e registry_url={}/v2 -e registry_readonly=false \\\n" +
                            "-e delete_enabled=true -e registry_name={} \\\n" +
                            "-p {}:8080 \\\n" +
                            "hyper/docker-registry-web",url,url,webPort);
                    System.out.println(registryWebInfo);
                    remoteSshUtil.exec(registryInfo,session);
                    remoteSshUtil.exec(registryWebInfo,session);

                    while (true) {
                        Thread.sleep(1000);
                        int i = 0;
                        List<Container> containerList = dockerClient.listContainersCmd().exec();
                        for (Container container : containerList) {
                            if (container.getImage().contains("registry") && !container.getImage().contains("registry-web")  && "running".equals(container.getState())){
                                i++;
                            }
                            if (container.getImage().contains("registry-web") && "running".equals(container.getState())){
                                i++;
                            }
                        }
                        if (i>=2){
                            Container container1 = containerList.stream().filter(container -> container.getImage().contains("registry") && !container.getImage().contains("registry-web")).findFirst().get();
                            dockerRegistry.setContainerId(container1.getId());
                            System.out.println("docker仓库安装成功"+JacksonUtils.toString(dockerRegistry));
                            break;
                        }
                    }
                    dockerRegistry.setInstallTime(new Date());
                    dockerRegistry.setUpdateTime(new Date());
                    admDockerRegistryMapper.updateById(dockerRegistry);
                    sendMsg("complete",registryFile.getOriginalFilename());
                    sendMsg("complete",registryWebFile.getOriginalFilename());
                    dockerUtil.close(dockerClient);
                    remoteSshUtil.close(session);
                } catch (Exception e) {
                    log.error(e.getMessage());
                    alertFailMsg(e.getMessage());
                } finally {
                    mainLatch.countDown();
                }
            });
        }
        try {
            mainLatch.await();
            System.out.println("docker仓库全部安装完毕");
            alertSuccessMsg("completeAll");
        }catch (InterruptedException e) {
            log.error(e.getMessage());
        }
    }

    /**
     * 重新启动注册表
     *
     * @param registryId 注册表主键id
     */
    @Override
    public void restartRegistry(Long registryId) {
        AdmDockerRegistry dockerRegistry = this.getById(registryId);
        AdmDocker admDocker = admDockerService.getByRegistryId(registryId);
        DockerClient dockerClient = dockerUtil.getDockerClientByDocker(SpringUtils.getBean(AdmDockerServiceImpl.class).convertToVo(admDocker));
        dockerClient.restartContainerCmd(dockerRegistry.getContainerId()).exec();
        dockerUtil.close(dockerClient);
        //log.info("重启成功:{}", JacksonUtils.toString(dockerRegistry));
    }

    private void sendMsg(String status,String name){
        websocketEndPoint.send(WsSendDto.builder().type(0).table("registryInstall").status(status).name(name).build());
    }

    /**
     * 弹窗提示
     * @param data
     */
    private void alertSuccessMsg(String data){
        websocketEndPoint.send(WsSendDto.builder().type(2).table("registryInstall").data(data).build());
    }
    private void alertFailMsg(String data){
        websocketEndPoint.send(WsSendDto.builder().type(3).table("registryInstall").data(data).build());
    }

}
